<template>
    <div class="full-percent forbid-select" ref="el" v-loading="loading">
        <el-scrollbar style="width: 100%;height: 100%;border-radius: 5px;">
            <div class="toolbar">
                <el-button class="button-auto-resize" :icon="getIcon('Position')" :disabled="loading" circle
                    @click="runInterface" v-permission.execute />
                <el-button class="button-auto-resize" :icon="getIcon('view')" circle @click="showSourceCode = true"
                    :disabled="loading || !code" v-if="props.interfaceInfo.type === 'sql'" />
            </div>
            <div style="display: flex; padding: 5px; flex-wrap: wrap; gap: 5px;" ref="titleRef">
                <div class="address">
                    <div class="title">接口地址：</div>
                    <el-tag style="min-width: 100px" disable-transitions @dblclick="copy">
                        {{ apiPrefix + props.interfaceInfo.url }}
                    </el-tag>
                </div>
                <div style="display: flex" class="method">
                    <div class="title">接口方法：</div>
                    <el-tag style="min-width: 50px" disable-transitions>{{ upper(props.interfaceInfo.method) }}</el-tag>
                </div>
            </div>
            <el-collapse v-model="activeNames" class="collapse">
                <el-collapse-item title="请求参数" name="1" class="title-h2 params" ref="requestRef">
                    <el-table :data="interfaceParams" border header-cell-class-name="el-table-header-cell"
                        highlight-current-row cell-class-name="el-table-cell" class="e-page request-table"
                        empty-text="无请求参数">
                        <el-table-column prop="name" label="参数名称" min-width="20%" />
                        <el-table-column prop="content" label="参数说明" min-width="20%" />
                        <el-table-column prop="require" label="是否必填" min-width="20%">
                            <template #default="scope">
                                {{ scope.row.require === 1 || scope.row.require === '1' ? '是' : '否' }}
                            </template>
                        </el-table-column>
                        <el-table-column prop="value" label="参数值" min-width="40%" class-name="params-value">
                            <template #default="scope">
                                <el-input size="small" v-model="scope.row.value" />
                                <div :class="`errorItem animate__animated ${error ? 'animate__tada' : ''}`"
                                    v-show="scope.row.require in [1, '1'] && !scope.row.value && scope.row.value !== 0">
                                    必填项不能为空
                                </div>
                            </template>
                        </el-table-column>
                    </el-table>
                </el-collapse-item>
                <el-collapse-item title="响应参数" name="2" class="title-h2 response" ref="responseRef">
                    <el-row>
                        <el-table :data="returnParams" border header-cell-class-name="el-table-header-cell"
                            cell-class-name="el-table-cell" class="e-page request-table" highlight-current-row>
                            <el-table-column prop="name" label="参数名称" width="225" />
                            <el-table-column prop="type" label="参数类型" width="200">
                                <template #default="scope">
                                    <span>{{
                                        scope.row.name === 'data' ? props.interfaceInfo.return_type === 'unknown' ?
                                            '具体以返回的数据类型为准' :
                                            props.interfaceInfo.return_type : scope.row.type
                                    }}</span>
                                </template>
                            </el-table-column>
                            <el-table-column prop="content" label="参数说明" min-width="520">
                                <template #default="scope">
                                    <div
                                        v-if="scope.row.name === 'data' && props.interfaceInfo.response && JSON.parse(props.interfaceInfo.response).length > 0">
                                        <el-table :data="JSON.parse(props.interfaceInfo.response)" style="width: 100%;"
                                            border header-cell-class-name="el-table-header-cell"
                                            cell-class-name="el-table-cell" class="e-page">
                                            <el-table-column prop="name" label="参数名称" min-width="35%" />
                                            <el-table-column prop="type" label="参数类型" min-width="20%" />
                                            <el-table-column prop="comment" label="参数说明" min-width="45%" />
                                        </el-table>
                                    </div>
                                    <div v-else>
                                        {{ scope.row.content }}
                                    </div>
                                </template>
                            </el-table-column>
                            <el-table-column prop="default" label="默认值" width="150" />
                        </el-table>
                    </el-row>
                </el-collapse-item>
                <el-collapse-item name="3" disabled style="position:relative;" class="return-data title-h2">
                    <template #title>
                        <div style="height: 100%;" ref="resultTitleRef">返回数据</div>
                    </template>
                    <div class="cost" v-if="cost >= 0">请求耗时{{ cost }}ms</div>
                    <aceEditor v-model="returnData" :options="{ readOnly: true, lang: 'json' }"
                        :style="`height: ${height}px;`" />
                </el-collapse-item>
            </el-collapse>
        </el-scrollbar>
        <el-drawer v-model="showSourceCode" direction="rtl" :show-close="false" :close-on-press-escape="false"
            size="50%" append-to-body :with-header="false">
            <aceEditor v-model="code" :options="{ readOnly: true, lang: 'sql' }" />
        </el-drawer>
    </div>
</template>

<script setup>
import { apiPrefix } from "@/utils/constant"
import { formatJson, deepCopy, getElementSize, getIcon } from "@/utils/tools"
import { useElMessage, useCopyText, useRequest } from "@usehook"
import { useResizeObserver } from '@vueuse/core'
import { format } from 'sql-formatter'

const props = defineProps({
    interfaceInfo: {
        type: Object,
        require: true
    }
})

const requestRef = ref(null)
const responseRef = ref(null)
const titleRef = ref(null)
const resultTitleRef = ref(null)
const el = ref(null)
let height = $ref(0)

useResizeObserver([requestRef, responseRef, titleRef, resultTitleRef, el], () => {
    const requestRefHeight = getElementSize(requestRef).height || 0
    const responseRefHeight = getElementSize(responseRef).height || 0
    const titleRefHeight = getElementSize(titleRef).height || 0
    const resultTitleRefHeight = getElementSize(resultTitleRef).height || 0
    const elHeight = getElementSize(el).height || 0
    const _height = elHeight - titleRefHeight - resultTitleRefHeight - requestRefHeight - responseRefHeight - 1
    height = requestRefHeight + responseRefHeight + titleRefHeight > elHeight ? 500 : _height > 200 ? _height : 200
})

let activeNames = $ref(['3'])
let returnParams = [
    { name: "curPage", content: "当前页数, 0表示未开启分页功能", default: "0", type: "int", value: "" },
    { name: "curRecords", content: "当前页记录数", default: "1", type: "int", value: "" },
    { name: "data", content: "返回的数据", default: "", type: "", value: "" },
    { name: "message", content: "描述", default: "查询成功", type: "string", value: "" },
    { name: "pageSize", content: "每页显示条数", default: "10", type: "int", value: "" },
    { name: "status", content: "响应状态", default: "true", type: "boolean", value: "" },
    { name: "totalPage", content: "总页数", default: "-1", type: "int", value: "" },
    { name: "totalRecords", content: "总条数", default: "1", type: "int", value: "" },
    { name: "responseStartTime", content: "请求开始时间", default: "sysdate", type: "string", value: "" },
    { name: "responseEndTime", content: "请求结束时间", default: "sysdate", type: "string", value: "" },
    { name: "version", content: "版本号", default: "-1", type: "string", value: "" }
]
let returnData = $ref("")
let cost = $ref(-1)
let error = $ref(false)
let loading = $ref(false)
let timeout = $ref(null)
let showSourceCode = $ref(false)
let code = $ref("")

watch(() => props.interfaceInfo.params, params => {
    if (params.length > 0) {
        if (!activeNames.includes("1")) activeNames.push("1")
    } else {
        let index = activeNames.indexOf("1")
        if (index >= 0) activeNames.splice(index, 1)
    }
}, { immediate: true })

const interfaceParams = $computed(() => {
    const params = (deepCopy(props.interfaceInfo.params) || []).filter(p => {
        return p.require !== 3
    })
    for (let p of params) {
        p.value = p.value || p.default
    }
    if (props.interfaceInfo.type === "sql") {
        params.push({
            content: "当前第几页",
            value: "1",
            name: "page",
            require: 2
        })
        params.push({
            content: "每页显示多少条",
            value: "10",
            name: "limit",
            require: 2
        })
    }
    return params
})

const errorCaution = () => {
    clearTimeout(timeout)
    error = true
    timeout = setTimeout(() => {
        error = false
    }, 1000)
}

const copy = (e) => {
    useCopyText(e.target.innerText).then(() => {
        useElMessage.success("接口地址复制成功")
    })
}

function adjustSpacesPerLine(str) {
    // 使用正则表达式分割字符串为数组，每个元素是原字符串中的一行  
    // 注意：这里假设换行符是\n，但在Windows系统中可能是\r\n  
    const lines = str.split(/\r?\n/);

    // 遍历每一行，根据条件进行空格的添加或删除  
    const adjustedLines = lines.map(line => {
        // 如果行首有三个或更多空格，则删除前三个空格  
        if (/^\s{3,}/.test(line)) {
            return line.replace(/^\s{3}/, '');
        }
        // 如果行首没有空格，则添加两个空格  
        else if (!/^\s/.test(line)) {
            return '  ' + line;
        }
        // 如果行首有少于三个空格，则保持不变（这一步其实是可选的，因为如果没有修改就不会进入这个分支）  
        return line;
    });

    // 将处理后的行数组重新组合为一个字符串，每行之间用换行符分隔  
    return adjustedLines.join('\n');
}

const runInterface = () => {
    let params = {}
    for (let p of interfaceParams) {
        if (p.require === 1 && (p.value === '' || p.value === null || p.value === undefined)) {
            if (!("1" in activeNames)) {
                activeNames.push("1")
                setTimeout(() => {
                    errorCaution()
                }, 150)
            } else {
                errorCaution()
            }
            return
        }
        params[p.name] = p.value
    }

    const lastRunTime = sessionStorage.getItem("lastRunTime")
    if (lastRunTime && new Date().getTime() - parseInt(lastRunTime) < 1000) {
        useElMessage.error("请求过于频繁, " + Math.ceil((1 - (new Date().getTime() - parseInt(lastRunTime)) / 1000)) + "s后在尝试")
        return
    } else {
        sessionStorage.setItem("lastRunTime", new Date().getTime().toString())
    }

    loading = true

    useRequest(apiPrefix + props.interfaceInfo.url, props.interfaceInfo.method, { ...params }).then(result => {
        if (result instanceof Object) {
            let { sourceCode, ..._returnData } = result
            if (props.interfaceInfo.type === "sql") {
                code = adjustSpacesPerLine(format(sourceCode, {
                    language: 'plsql',
                    tabWidth: 5,
                    useTabs: true,
                    keywordCase: "lower",
                    identifierCase: "lower",
                    dataTypeCase: "lower",
                    functionCase: "lower",
                    indentStyle: "tabularRight",
                    logicalOperatorNewline: "before",
                    linesBetweenQueries: 5,
                    denseOperators: true,
                    newlineBeforeSemicolon: false
                }) || "")
            } else {
                code = sourceCode
            }
            cost = new Date(_returnData.responseEndTime).getTime() - new Date(_returnData.responseStartTime).getTime()
            returnData = formatJson(_returnData)
        } else {
            returnData = result.toString()
        }

    }).catch((e) => {
        returnData = formatJson(e)
    }).finally(() => {
        loading = false
    })
}
const upper = (val) => {
    if (val) {
        return val.toUpperCase()
    }
    return ""
}
</script>

<style lang="scss" scoped>
:deep(.request-table) {
    width: 100%;
    font-size: var(--font-size);

    @media screen and (min-width: 200px) and (max-width: 400px) {
        width: 97%;
    }
}

:deep(.address) {
    margin-right: 20px;
    display: flex;
    align-items: center;
    justify-content: center;
}

:deep(.el-scrollbar__wrap) {
    overflow-x: hidden;
}

:deep(.toolbar) {
    position: absolute;
    right: 3px;
    top: 3px;
    z-index: 999;
}

:deep(.result) {
    margin-left: 5px;
}

:deep(.title) {
    min-width: 100px;
    height: 100%;
    font-size: var(--font-size);
    justify-content: center;
    align-items: center;
    display: flex;
}

:deep(.el-tag) {
    font-size: var(--font-size);
    padding: 10px;
    height: fit-content;
}

:deep(.el-collapse-item__arrow) {
    margin: 1px 0 0 10px;
}

:deep(.el-collapse-item__content) {
    padding-bottom: 5px;
}

:deep(.el-collapse-item__content:last-child) {
    padding-bottom: 0;
}

:deep(.el-collapse-item__header) {
    height: 45px;
    font-size: 13px;
    /*width: 75px;*/
    border-bottom: none;
    padding-left: 10px;
}

:deep(.e-page) {
    margin-top: 5px;
}

:deep(div[role="tab"]) {
    border-bottom: 1px solid var(--border-color);
}

:deep(.errorItem) {
    right: 5%;
    font-size: calc(var(--font-size) - 4px);
    left: unset;
    position: absolute;
    color: var(--error-font-color);
    cursor: default;
    pointer-events: none;
    /*transition: all .4s ease-in;*/
}

/*:deep(.error) {*/
/*  top: 22%;*/
/*  right: 8%;*/
/*  font-size: 18px;*/
/*  transition: all .1s ease-in;*/
/*}*/

:deep(.el-drawer__header) {
    margin: 0 !important;
    padding: 0;
}

:deep(.el-scrollbar__bar.is-vertical) {
    width: 0;
}

:deep(.el-drawer__body) {
    overflow: hidden;
}

:deep(.el-collapse-item.is-disabled .el-collapse-item__header) {
    cursor: default;
    color: #303133;
}

:deep(.is-disabled .el-collapse-item__arrow) {
    display: none;
}

:deep(.empty-tag) {
    display: block;
    margin: 5px auto 0 auto;
    text-align: center;
    border: 1px solid var(--border-color);
    border-radius: 5px;
    width: 99%;
}

:deep(.comment) {
    position: absolute;
    left: 200px;
    top: 12px;
    /*font-size: 16px;*/
    cursor: default;
    z-index: 999;
    border-radius: 15px;
}

:deep(.title-header .el-collapse-item__header) {
    font-size: calc(var(--font-size) + 3px);
    font-weight: var(--font-weight);
}

:deep(.title-h2 .el-collapse-item__header) {
    font-size: var(--font-size);
    font-weight: var(--font-weight);
}

:deep(.el-collapse-item__header) {
    color: #80c0ed !important;
}

:deep(.el-textarea__inner),
:deep(.el-textarea) {
    height: calc(100vh - 250px) !important;
}

:deep(.hljs) {
    padding: 5px 10px;
}

:deep(pre),
:deep(code) {
    font-family: inherit;
    font-size: var(--font-size);
    height: fit-content;
    margin: 0;
    overflow: auto;
    background: rgb(248, 248, 248);
}

:deep(.el-button + .el-button) {
    margin-left: 10px !important;
    ;
}

.return-data :last-child {
    border: 0 !important;
}

:deep(.e-page) {
    margin-bottom: -1px;
}

:deep(.el-collapse-item__content .el-row) {
    margin-top: 0 !important;
}

:deep(.el-table .cell) {
    height: fit-content;
    line-height: inherit;
    font-size: var(--font-size);
}

:deep(.params .params-value .cell) {
    display: flex;
    justify-content: center;
    align-items: center;
}

:deep(.cost) {
    position: absolute;
    top: 10px;
    right: 30px;
    font-size: calc(var(--font-size) - 3px);
}

:deep(.el-input__inner) {
    height: fit-content;
    font-family: var(--font-family);
    font-weight: var(--font-weight);
    padding: 5px;
}

:deep(.el-input--small) {
    font-size: var(--font-size);
}

:deep(.response) {
    @media screen and (min-width: 200px) and (max-width: 400px) {
        display: none;
    }
}
</style>